package com.example.socket.codec;

import com.example.socket.core.Message;
import com.example.socket.exception.DecodeException;
import io.netty.buffer.ByteBuf;
import io.netty.channel.Channel;
import io.netty.channel.ChannelHandlerContext;
import io.netty.handler.codec.LengthFieldBasedFrameDecoder;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.net.SocketAddress;
import java.util.Arrays;

/**
 * 通信消息({@link Message})解码器
 * @author frank
 */
public class MessageDecoder extends LengthFieldBasedFrameDecoder {

    private static final Logger logger = LoggerFactory.getLogger(MessageDecoder.class);

    public MessageDecoder(int maxLen) {
        super(maxLen, 0, 4, -4, 0);
    }

    @Override
    protected ByteBuf extractFrame(ChannelHandlerContext ctx, ByteBuf buffer, int index, int length) {
        // BUFF 丢弃已解码字节，回收BUFF空间
        if (logger.isDebugEnabled()) {
            Channel channel = ctx.channel();
            SocketAddress remoteAddress = channel.remoteAddress();
            logger.debug("连接[{}]已解码切片BUFF[{}:{}]", remoteAddress, index, length);
        }
        return buffer.slice(index, length);
    }

    @Override
    protected Object decode(ChannelHandlerContext ctx, ByteBuf in) throws Exception {
        if (in.readableBytes() == 0) {
            // BUFF为空
            return null;
        }
        Channel channel = ctx.channel();
        SocketAddress remoteAddress = channel.remoteAddress();
        if (logger.isDebugEnabled()) {
            logger.debug("连接[{}]准备解码BUFF[{}:{}]", remoteAddress, in.readerIndex(), in.readableBytes());
        }
        ByteBuf frame = (ByteBuf) super.decode(ctx, in);
        if (frame == null) {
            if (logger.isDebugEnabled()) {
                logger.debug("忽略连接[{}]的无效数据包 - 数据包不完整", remoteAddress);
            }
            return null;
        }
        try {
            Message message = Message.valueOf(frame);
            if (logger.isDebugEnabled()) {
                logger.debug("解码数据,会话:[{}] 头信息:[{}] 信息体:BYTES:{}", remoteAddress, message,
                        Arrays.toString(message.getBody()));
            }
            return message;
        } catch (Exception ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("连接[{}]解码错误", remoteAddress, ex);
            }
            // 解码错误, 断开连接
            ctx.close();
            logger.error("断开连接[{}], 解码错误{} - {}", remoteAddress, ex.getClass(), ex.getMessage());
            // 抛出解码错误
            throw new DecodeException(ex);
        }
    }
}